package com.jj.service.room;

import com.jj.battle.protocol.GameProto;
import com.jj.entity.RecorderManager;
import com.jj.service.player.Player;
import com.jj.thrift.client.VerifyClient;
import org.apache.commons.io.FileUtils;
import org.apache.thrift.TException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.io.File;
import java.nio.ByteBuffer;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Copyrights: QidianGroup@JJ
 *
 * @author: LuHan
 * @date: 2018/1/15, 16:21
 */
@Component
public class RoomManagerImpl implements RoomManager{
    private static Logger logger = LoggerFactory.getLogger(RoomManagerImpl.class);

    @Autowired
    private VerifyClient verifyClient;

    private volatile int maxRoomId = 1;
    private HashMap<Long, Room> rooms = new HashMap<>();
    private HashMap<Long, Room> player2Room = new HashMap<>();

    @PostConstruct
    public void ini(){
        logger.debug("verifyclient:{}", verifyClient);
    }

    public synchronized int getAndIncreaseRoomId(){
        int nextRoomId = maxRoomId;
        maxRoomId++;
        return nextRoomId;
    }

    public Room getRoom(Integer roomId){
        return rooms.get(roomId);
    }

    @Override
    public synchronized Room createRoom(long playerA, long playerB) {
        //Room room = new RoomImpl(tickerWorkerThread);
        Room room = new RoomImpl();
        room.setId(getAndIncreaseRoomId());
        room.setPlayerA(playerA);
        room.setPlayerB(playerB);
        rooms.put(room.getId(), room);
        player2Room.put(playerA, room);
        player2Room.put(playerB, room);
        logger.debug("create room {} with {} {}", room.getId(), playerA, playerB);
        return room;
    }

    @Override
    public synchronized void removeRoom(Room room) {
        rooms.remove(room.getId());
    }


    @Override
    public synchronized void leave(Player player) {
        if(player != null){
            leave(player.getUserId());
        }
    }

    @Override
    public synchronized void leave(Long uid) {
        Room room = player2Room.get(uid);
        if(room != null){
            logger.debug("player {} try to leave {}, size:{}", uid, room.getId(), room.size());
            room.leave(uid);
            player2Room.remove(uid);
            if(room.size() == 0) {
                logger.debug("room {} is empty, remove", room.getId());
                rooms.remove(room.getId());
                RecorderManager record = room.getRecorderManager();
                List<GameProto.PKG_FRAME>  frames = record.getSynState(0);
                GameProto.PKG_VERIFY.Builder verifyBuilder = GameProto.PKG_VERIFY.newBuilder();
                verifyBuilder.setPlayerId1(room.getPlayerA());
                verifyBuilder.setPlayerId2(room.getPlayerB());
                verifyBuilder.addAllAllFrames(frames);
                GameProto.PKG_VERIFY verify = verifyBuilder.build();
                byte[] data = verify.toByteArray();
                ByteBuffer buffer = ByteBuffer.wrap(data);
                try {
                    logger.debug("begin thrift call");
                    String ret = verifyClient.getClient().calc(buffer);
                    logger.debug("after thrift call:{}", ret);
                } catch (TException e) {
                    logger.debug("exception", e);
                }
            }else{
                logger.debug("room contains :{}, can not remove",
                        room.getPlayers().stream().map(player -> player.getUserId()).collect(Collectors.toList()));
            }
        }else{
            logger.warn("room not found for {}", uid);
        }
    }

    @Override
    public synchronized Room findPlayerRoom(Player player) {
        return player.getRoom();
    }

    @Scheduled(fixedRate = 1000)
    public synchronized void heartbeat(){
        //send verify
//        rooms.values().stream().filter(room -> room.needStop()).forEach(
//                room -> {
////                    RecorderManager record = room.getRecorderManager();
////                    List<GameProto.PKG_FRAME>  frames = record.getSynState(0);
////                    GameProto.PKG_VERIFY.Builder verifyBuilder = GameProto.PKG_VERIFY.newBuilder();
////                    verifyBuilder.setPlayerId1(room.getPlayerA());
////                    verifyBuilder.setPlayerId2(room.getPlayerB());
////                    verifyBuilder.addAllAllFrames(frames);
////                    GameProto.PKG_VERIFY verify = verifyBuilder.build();
//                    GameProto.PKG_VERIFY verify = room.buildVerify();
//                    byte[] data = verify.toByteArray();
//                    try{
//                        FileUtils.writeByteArrayToFile(new File("./data/battle.txt"), data);
//                    }catch (Throwable t){
//                        logger.debug("exception", t);
//                    }
//                    ByteBuffer buffer = ByteBuffer.wrap(data);
//                    try {
//                        logger.debug("begin thrift call");
//                        String ret = verifyClient.getClient().calc(buffer);
//                        logger.debug("after thrift call:{}", ret);
//                    } catch (TException e) {
//                        logger.debug("exception", e);
//                    }
//                }
//        );

        //remove dead room
        Iterator<Room> it = rooms.values().iterator();
        while(it.hasNext()){
            Room room = it.next();
            if(room.needStop()){
                rooms.remove(room.getId());
            }
        }

        //call heartbeat
        rooms.values().forEach(Room::heartbeat);
    }
}
